<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>揭秘浏览器中的"幽灵"双重POST请求</title>
    <link href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <style>
        body {
            font-family: 'Noto Sans SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            min-height: 100vh;
        }
        
        /* 首字下沉效果 */
        .drop-cap::first-letter {
            float: left;
            font-size: 4rem;
            line-height: 3rem;
            padding-right: 0.5rem;
            margin-top: 0.5rem;
            font-weight: 700;
            color: #4f46e5;
            font-family: 'Noto Serif SC', serif;
        }
        
        /* 代码块美化 */
        pre {
            background: #1e293b;
            color: #e2e8f0;
            border-radius: 12px;
            padding: 1.5rem;
            overflow-x: auto;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
            position: relative;
            margin: 2rem 0;
        }
        
        pre::before {
            content: attr(data-language);
            position: absolute;
            top: 0.5rem;
            right: 1rem;
            font-size: 0.75rem;
            color: #64748b;
            text-transform: uppercase;
            letter-spacing: 0.05em;
        }
        
        /* 卡片悬停效果 */
        .hover-card {
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        }
        
        .hover-card:hover {
            transform: translateY(-4px);
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
        }
        
        /* 标题装饰线 */
        .section-title::after {
            content: '';
            display: block;
            width: 60px;
            height: 4px;
            background: linear-gradient(90deg, #4f46e5 0%, #7c3aed 100%);
            margin-top: 1rem;
            border-radius: 2px;
        }
        
        /* 引用样式 */
        .quote-box {
            border-left: 4px solid #4f46e5;
            background: rgba(79, 70, 229, 0.05);
            padding: 1.5rem 2rem;
            margin: 2rem 0;
            border-radius: 0 8px 8px 0;
            position: relative;
        }
        
        .quote-box::before {
            content: '\f10d';
            font-family: 'Font Awesome 6 Free';
            font-weight: 900;
            position: absolute;
            top: -10px;
            left: 20px;
            font-size: 2rem;
            color: #4f46e5;
            opacity: 0.2;
        }
        
        /* 列表美化 */
        .custom-list li {
            position: relative;
            padding-left: 2rem;
            margin-bottom: 1rem;
        }
        
        .custom-list li::before {
            content: '\f00c';
            font-family: 'Font Awesome 6 Free';
            font-weight: 900;
            position: absolute;
            left: 0;
            color: #4f46e5;
        }
        
        /* 数字标记 */
        .number-badge {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 2.5rem;
            height: 2.5rem;
            background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
            color: white;
            border-radius: 50%;
            font-weight: 700;
            margin-right: 1rem;
            flex-shrink: 0;
        }
        
        /* 渐变文字 */
        .gradient-text {
            background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }
        
        /* 动画效果 */
        @keyframes fadeInUp {
            from {
                opacity: 0;
                transform: translateY(20px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }
        
        .animate-fadeInUp {
            animation: fadeInUp 0.6s ease-out forwards;
        }
        
        /* Mermaid图表样式 */
        .mermaid {
            background: white;
            padding: 2rem;
            border-radius: 12px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
            margin: 2rem 0;
        }
    </style>
</head>
<body>
    <!-- Hero Section -->
    <section class="relative overflow-hidden bg-gradient-to-br from-indigo-600 via-purple-600 to-pink-500 text-white">
        <div class="absolute inset-0 bg-black opacity-20"></div>
        <div class="relative container mx-auto px-6 py-24 text-center">
            <h1 class="text-5xl md:text-7xl font-bold mb-6 animate-fadeInUp">
                揭秘浏览器中的<br>
                <span class="text-yellow-300">"幽灵"双重POST请求</span>
            </h1>
            <p class="text-xl md:text-2xl mb-8 opacity-90 max-w-3xl mx-auto animate-fadeInUp" style="animation-delay: 0.2s">
                深入理解HTTP协议与浏览器工作原理，解开困扰开发者的网络请求谜团
            </p>
            <div class="flex justify-center space-x-6 animate-fadeInUp" style="animation-delay: 0.4s">
                <div class="flex items-center">
                    <i class="fas fa-network-wired text-3xl mr-3"></i>
                    <span class="text-lg">网络协议</span>
                </div>
                <div class="flex items-center">
                    <i class="fas fa-shield-alt text-3xl mr-3"></i>
                    <span class="text-lg">安全机制</span>
                </div>
                <div class="flex items-center">
                    <i class="fas fa-code text-3xl mr-3"></i>
                    <span class="text-lg">实战解决</span>
                </div>
            </div>
        </div>
        <div class="absolute bottom-0 left-0 right-0">
            <svg viewBox="0 0 1440 120" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M0 120L60 110C120 100 240 80 360 70C480 60 600 60 720 65C840 70 960 80 1080 85C1200 90 1320 90 1380 90L1440 90V120H1380C1320 120 1200 120 1080 120C960 120 840 120 720 120C600 120 480 120 360 120C240 120 120 120 60 120H0V120Z" fill="white"/>
            </svg>
        </div>
    </section>

    <!-- Main Content -->
    <main class="container mx-auto px-6 py-12 max-w-5xl">
        <!-- 引言 -->
        <div class="bg-white rounded-2xl shadow-xl p-8 mb-12 hover-card">
            <p class="text-lg leading-relaxed text-gray-700 drop-cap">
                你是否曾经在开发过程中发现一个奇怪的现象：明明只触发了一次表单提交，却在网络面板中看到两个POST请求？或者在调试API接口时，一个简单的数据提交却导致服务器收到重复数据？这不是你的代码出了问题，而是浏览器的一种特殊行为机制。
            </p>
            <div class="mt-6 p-6 bg-gradient-to-r from-indigo-50 to-purple-50 rounded-xl">
                <p class="text-gray-700 leading-relaxed">
                    当我第一次遇到这个问题时，也是一头雾水，直到深入了解了HTTP协议和浏览器的工作原理，才恍然大悟。今天，就让我们一起揭开这个困扰许多开发者的谜团，看看那些"幽灵般"的双重POST请求到底是怎么回事。
                </p>
            </div>
        </div>

        <!-- 数据可视化：请求流程图 -->
        <div class="bg-white rounded-2xl shadow-xl p-8 mb-12">
            <h3 class="text-2xl font-bold mb-6 gradient-text">请求流程可视化</h3>
            <div class="mermaid">
                graph LR
                    A[浏览器] -->|1. OPTIONS预检请求| B[服务器]
                    B -->|2. 返回CORS头部| A
                    A -->|3. 实际POST请求| B
                    B -->|4. 返回响应数据| A
                    
                    style A fill:#4f46e5,stroke:#333,stroke-width:2px,color:#fff
                    style B fill:#7c3aed,stroke:#333,stroke-width:2px,color:#fff
            </div>
        </div>

        <!-- Section 1: OPTIONS预检请求 -->
        <section class="mb-16">
            <h2 class="text-3xl font-bold mb-8 section-title">
                <span class="number-badge">1</span>
                OPTIONS预检请求
            </h2>

            <div class="grid md:grid-cols-2 gap-8 mb-8">
                <div class="bg-white rounded-xl shadow-lg p-6 hover-card">
                    <h3 class="text-xl font-semibold mb-4 flex items-center">
                        <i class="fas fa-globe text-indigo-600 mr-3"></i>
                        什么是CORS跨域资源共享
                    </h3>
                    <p class="text-gray-700 leading-relaxed mb-4">
                        跨域资源共享(CORS)是浏览器的一种安全机制，用于控制不同源之间的HTTP请求。当你的前端应用尝试向不同源的服务器发送请求时，浏览器会执行CORS检查。
                    </p>
                    <div class="bg-gray-50 p-4 rounded-lg">
                        <p class="text-sm text-gray-600 mb-2">同源要求：</p>
                        <ul class="custom-list text-sm">
                            <li>协议相同（http/https）</li>
                            <li>域名相同</li>
                            <li>端口相同</li>
                        </ul>
                    </div>
                </div>

                <div class="bg-white rounded-xl shadow-lg p-6 hover-card">
                    <h3 class="text-xl font-semibold mb-4 flex items-center">
                        <i class="fas fa-shield-alt text-purple-600 mr-3"></i>
                        预检请求的作用
                    </h3>
                    <p class="text-gray-700 leading-relaxed mb-4">
                        预检请求是浏览器在发送实际跨域请求前，先发送一个OPTIONS方法的HTTP请求，用来"询问"服务器是否允许接下来的实际请求。
                    </p>
                    <div class="bg-purple-50 p-4 rounded-lg">
                        <p class="text-sm text-gray-600 mb-2">主要作用：</p>
                        <ul class="custom-list text-sm">
                            <li>保护服务器免受有害请求</li>
                            <li>验证请求方法和头部</li>
                            <li>确认内容类型支持</li>
                        </ul>
                    </div>
                </div>
            </div>

            <div class="quote-box">
                <p class="text-gray-700">
                    <strong>为什么需要OPTIONS请求？</strong><br>
                    浏览器发送OPTIONS请求是为了确认服务器是否允许该跨域请求、检查服务器是否接受请求中使用的HTTP方法、验证服务器是否接受请求中携带的自定义头部，以及确认服务器是否允许请求中的内容类型。
                </p>
            </div>

            <pre data-language="http">OPTIONS /api/data HTTP/1.1
Host: api.otherservice.com
Origin: https://myapp.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization</pre>
        </section>

        <!-- Section 2: 常见触发场景 -->
        <section class="mb-16">
            <h2 class="text-3xl font-bold mb-8 section-title">
                <span class="number-badge">2</span>
                常见的触发双重POST场景
            </h2>

            <div class="space-y-6">
                <div class="bg-gradient-to-r from-blue-50 to-indigo-50 rounded-xl p-6">
                    <h3 class="text-xl font-semibold mb-4">
                        <i class="fas fa-code text-blue-600 mr-2"></i>
                        跨域API调用
                    </h